//
// Created by gj on 16-3-16.
//

#include "Rc4Object.h"


void Rc4SBoxKey::update(int32_t keySize, const uint8_t *key) {
    clear();
    this->keySize = keySize;
    this->key = new uint8_t[keySize];
    memcmp(this->key, key, keySize);
}

Rc4SBoxConfig *Rc4SBoxConfig::_instance = NULL;

void Rc4SBoxConfig::init(Rc4KeyHandler &nativeRc4KeyHandler, Rc4KeyHandler &dynamicRc4KeyHandler) {
    if (_instance != NULL) {
        delete _instance;
    }
    _instance = new Rc4SBoxConfig(nativeRc4KeyHandler, dynamicRc4KeyHandler);
}

void Rc4SBoxConfig::updateKey(const uint8_t *key, int32_t keySize) {
    AutoMutex autoMutex(mutex);
    // 判断key是否有效
    if (key == NULL || keySize <= 0) {
        // 无效继续禁用
        _updateKeyForNative();
    } else {
        // 有效则保存key
        rc4Key.update(keySize, key);
        dynamicRc4KeyHandler->saveRc4Key(rc4Key, false);
        time(&keyUpdateTime);
        isDisable = false;
    }
    bzero(s_box, RC4_SBOX_NUM);
    if (!rc4Key.isInvalid()) {
        // 初始化sbox
        rc4_init(rc4Key.getKey(), rc4Key.getKeySize(), s_box, RC4_SBOX_NUM);
    }
}

void Rc4SBoxConfig::disable(bool &d, time_t &t, uint8_t *s_box) {
    AutoMutex autoMutex(mutex);

    if (isDisable || (!d && t == keyUpdateTime)) {
        rc4Key.clear();
        time(&keyUpdateTime);
        isDisable = !dynamicRc4KeyHandler->obtainRc4Key(rc4Key, true);
    }

    _copy(d, t, s_box);
}

void Rc4SBoxConfig::_init() {
    isDisable = false;
    rc4Key.clear();
    bzero(s_box, RC4_SBOX_NUM);
    bzero(s_box_native, RC4_SBOX_NUM);
    _updateKeyForNative();
    rc4_init(rc4Key.getKey(), rc4Key.getKeySize(), s_box, RC4_SBOX_NUM);
    rc4_init(rc4Key.getKey(), rc4Key.getKeySize(), s_box_native, RC4_SBOX_NUM);
    isDisable = !dynamicRc4KeyHandler->obtainCacheRc4Key(rc4Key, false);
    if (!isDisable) {
        rc4_init(rc4Key.getKey(), rc4Key.getKeySize(), s_box, RC4_SBOX_NUM);
    } else {
        rc4Key.clear();
        isDisable = !dynamicRc4KeyHandler->obtainRc4Key(rc4Key, true);
    }
}


void Rc4SBoxConfig::_updateKeyForNative() {
    if (isDisable) {
        return;
    }
    nativeRc4KeyHandler->obtainRc4Key(rc4Key, false);
    if (rc4Key.isInvalid()) {
        nativeRc4KeyHandler->obtainCacheRc4Key(rc4Key, false);
    }
    time(&keyUpdateTime);
    isDisable = true;
}


void Rc4SBoxConfig::_copy(bool &d, time_t &t, uint8_t *s_box) const {
    if (rc4Key.isInvalid()) {
        d = true;
        time(&t);
        memcpy(s_box, this->s_box_native, RC4_SBOX_NUM);
    } else {
        d = isDisable;
        t = keyUpdateTime;
        memcpy(s_box, this->s_box, RC4_SBOX_NUM);
    }
}

void Rc4Object::update() {
    Rc4SBoxConfig *config = Rc4SBoxConfig::getInstance();
    if (keyUpdateTime == 0) {
        config->copySBox(isDisable, keyUpdateTime, s_box);
    } else {
        config->disable(isDisable, keyUpdateTime, s_box);
    }
}

uint8_t *Rc4Object::startRc4(uint8_t *data, int32_t size) {
    if (data == NULL || size == 0) {
        return data;
    }
    uint8_t _s_box[RC4_SBOX_NUM];
    memcpy(_s_box, this->s_box, RC4_SBOX_NUM);
    data = rc4_crypt(data, size, _s_box, RC4_SBOX_NUM);
    if (isDisable) {
        update();
    }
    return data;
}


// --------------------------------------------- unit test --------------------------------------------------
#ifdef FLAG_UNIT_TESTING

void rc4_unit_testing() {

}

#endif